// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: hyuan

#pragma once

#include <iomanip>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <assert.h>

#include "coin2/base/time.h"
#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/api_base/base_auth.h"
#include "coin2/exchange/base/api_util/api_signature.h"
#include "coin2/exchange/base/api_util/auth_key.h"
#include "coin2/exchange/base/api_util/json_writer.h"
#include "coin2/exchange/base/api_util/url_params_builder.h"

class DeribitBaseAuth {
 public:
  DeribitBaseAuth(const std::string& key_filepath) : key_(AuthKey::FromFile(key_filepath)) {
    header_.push_back(std::make_pair("Content-Type", "application/json"));
    header_.push_back(std::make_pair("Authorization", ""));
  }
  virtual ~DeribitBaseAuth() = default;

  bool has_auth_token() { return !auth_token_.empty() && !refresh_token_.empty(); }

  bool get_is_refreshing_token() { return is_refreshing_token_; }
  void set_is_refreshing_token() {
    LOG(INFO) << "refreshing_token prev auth_token:" << auth_token_ << "\n"
              << "prev refresh_token:" << refresh_token_;
    last_refresh_ts_ = GetCurrentTimestamp();
    is_refreshing_token_ = true;
  }

  auto last_set_token_ts() -> int64_t { return last_set_token_ts_; }
  auto last_refresh_ts() -> int64_t { return last_refresh_ts_; }

  void set_token(const std::string& auth_token, const std::string& refresh_token) {
    last_set_token_ts_ = GetCurrentTimestamp();
    auth_token_ = auth_token;
    refresh_token_ = refresh_token;
    is_refreshing_token_ = false;
    LOG(INFO) << "refresh_token done new auth_token:" << auth_token_ << "\n"
              << "new refresh_token:" << refresh_token_;
  }

  auto GetHeaderSet() -> const std::vector<std::pair<std::string, std::string>>& {
    header_[1].second = fmt::format("Bearer {}", auth_token_);
    return header_;
  }

  auto GetAccessKey() const -> const std::string& { return key_.access_key(); }
  auto GetSecretKey() const -> const std::string& { return key_.secret_key(); }
  auto GetRefreshToken() const -> const std::string& { return refresh_token_; }

 protected:
  AuthKey key_;
  std::vector<std::pair<std::string, std::string>> header_;

  std::string auth_token_;
  std::string refresh_token_;
  bool is_refreshing_token_ = false;
  int64_t last_set_token_ts_ = 0;
  int64_t last_refresh_ts_ = 0;
};

class DeribitRestAuth : public DeribitBaseAuth, public BaseRestAuth {
 public:
  DeribitRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      const std::string& port,
      const std::string& bind_ip)
      : DeribitBaseAuth(key_filepath), host_(host), port_(port), bind_ip_(bind_ip) {}
  DeribitRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      int32_t port,
      const std::string& bind_ip)
      : DeribitBaseAuth(key_filepath),
        host_(host),
        port_(std::to_string(port)),
        bind_ip_(bind_ip) {}

  AsioHttpContext* CreateRequestByGetWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    std::string query_string = builder->ComputeQueryString();
    std::string post_body = builder->ComputePostBody();
    CHECK(post_body.empty());

    std::string target;
    if (!query_string.empty()) {
      target = address + "?" + query_string;
    } else {
      target = address;
    }
    return AsioHttpContext::NewGet(host_, port_, bind_ip_, target, GetHeaderSet());
  }

  AsioHttpContext* CreateRequestByPostWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

  AsioHttpContext* CreateRequestByDeleteWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

  AsioHttpContext* CreateRequestByPutWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

 protected:
  std::string host_;
  std::string port_;
  std::string bind_ip_;
};
